package com.example.project.projectspringcloud.common.thymeleaf;

import com.example.project.projectspringcloud.dao.sys.AreaMapper;
import com.example.project.projectspringcloud.dao.sys.DictionaryMapper;
import com.example.project.projectspringcloud.dao.sys.SysConfigMapper;
import com.example.project.projectspringcloud.entity.sys.Area;
import com.example.project.projectspringcloud.entity.sys.Dictionary;
import com.example.project.projectspringcloud.entity.sys.SysConfig;
import com.example.project.projectspringcloud.utils.ObjectUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.thymeleaf.context.ITemplateContext;
import org.thymeleaf.dialect.AbstractProcessorDialect;
import org.thymeleaf.engine.AttributeName;
import org.thymeleaf.exceptions.TemplateProcessingException;
import org.thymeleaf.model.IProcessableElementTag;
import org.thymeleaf.processor.IProcessor;
import org.thymeleaf.processor.element.AbstractAttributeTagProcessor;
import org.thymeleaf.processor.element.IElementTagStructureHandler;
import org.thymeleaf.standard.expression.IStandardExpression;
import org.thymeleaf.standard.expression.IStandardExpressionParser;
import org.thymeleaf.standard.expression.StandardExpressionParser;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.util.EvaluationUtils;
import org.thymeleaf.util.Validate;

import java.math.BigDecimal;
import java.util.*;

public class CustomDialect extends AbstractProcessorDialect {

    @Autowired
    private DictionaryMapper dictionaryMapper;

    @Autowired
    private SysConfigMapper sysConfigMapper;

    @Autowired
    private AreaMapper areaMapper;

    private static final String PREFIX = "cus";

    public CustomDialect() {
        super("CustomDialect", PREFIX, 1000);
    }

    @Override
    public Set<IProcessor> getProcessors(final String dialectPrefix) {
        final LinkedHashSet<IProcessor> processors = new LinkedHashSet<>();
        processors.add(new StringProcessor(dialectPrefix));
        processors.add(new DictionaryProcessor(dialectPrefix, dictionaryMapper));
        processors.add(new SysConfigProcessor(dialectPrefix, sysConfigMapper));
        processors.add(new GetAreaProcessor(dialectPrefix, areaMapper));
        processors.add(new StarProcessor(dialectPrefix));
        processors.add(new NumberProcessor(dialectPrefix));
        return processors;
    }

}

/**
 * 自定义字符串处理
 */
class StringProcessor extends AbstractAttributeTagProcessor {

    private static final String ATTRIBUTE_NAME = "space";
    private static final int PRECEDENCE = 10000;

    public StringProcessor(final String dialectPrefix) {
        super(TemplateMode.HTML, dialectPrefix,
                null, false,
                ATTRIBUTE_NAME, true,
                PRECEDENCE, true);
    }

    @Override
    public void doProcess(ITemplateContext context, IProcessableElementTag tag,
                          AttributeName attributeName, String attributeValue,
                          IElementTagStructureHandler structureHandler) {
        String[] params = attributeValue.split(",");
        Integer each = (Integer) ThymeleafFacade.evaluateExpression(context, params[1]);
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < each; i++) {
            sb.append(params[0]);
        }
        structureHandler.replaceWith(sb.toString(), false);
    }
}

/**
 * 自定义评价星星
 */
class StarProcessor extends AbstractAttributeTagProcessor {

    private static final String ATTRIBUTE_NAME = "star";
    private static final int PRECEDENCE = 10000;

    public StarProcessor(final String dialectPrefix) {
        super(TemplateMode.HTML, dialectPrefix,
                null, false,
                ATTRIBUTE_NAME, true,
                PRECEDENCE, true);
    }

    @Override
    public void doProcess(ITemplateContext context, IProcessableElementTag tag,
                          AttributeName attributeName, String attributeValue,
                          IElementTagStructureHandler structureHandler) {
        String[] params = attributeValue.split(",");
        // params[0] 标签,  params[1] 普通class, params[2] 星星class, params[3] 数量 params[4] 总数量
        BigDecimal star = (BigDecimal) ThymeleafFacade.evaluateExpression(context, params[3]);
        if (star == null) {
            star = new BigDecimal(1);
        }
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i <= Integer.parseInt(params[4]); i++) {
            if (i <= star.intValue()) {
                sb.append(String.format("<%s class='%s'></%s>", params[0], params[2], params[0]));
            } else {
                sb.append(String.format("<%s class='%s'></%s>", params[0], params[1], params[0]));
            }
        }
        structureHandler.replaceWith(sb.toString(), false);
    }
}

/**
 * 字典处理
 */
class DictionaryProcessor extends AbstractAttributeTagProcessor {

    private DictionaryMapper dictionaryMapper;

    private static final String ATTRIBUTE_NAME = "dict";
    private static final int PRECEDENCE = 10000;

    public DictionaryProcessor(final String dialectPrefix, final DictionaryMapper dictionaryMapper) {
        super(TemplateMode.HTML, dialectPrefix,
                null, false,
                ATTRIBUTE_NAME, true,
                PRECEDENCE, true);
        this.dictionaryMapper = dictionaryMapper;
    }

    @Override
    public void doProcess(ITemplateContext context, IProcessableElementTag tag,
                          AttributeName attributeName, String attributeValue,
                          IElementTagStructureHandler structureHandler) {
        // params[0] 类型, params[1] 值
        String[] params = attributeValue.split(",");

        Object param = ThymeleafFacade.evaluateExpression(context, params[1]);
        if (ObjectUtil.isNull(param)) {
            structureHandler.removeElement();
            return;
        }
        Dictionary entity = new Dictionary();
        entity.setType(params[0]);
        entity.setValue((Integer) param);
        entity = dictionaryMapper.selectOne(entity);

        // 有前值
        if (params.length == 3) {
            structureHandler.setBody(params[2] + entity.getName(), false);
        }
        // 有前值和后值
        else if (params.length == 4) {
            structureHandler.setBody(params[2] + entity.getName() + params[3], false);
        }
        // 无前值和后值
        else {
            structureHandler.setBody(entity.getName(), false);
        }
    }
}

/**
 * 配置获取
 */
class SysConfigProcessor extends AbstractAttributeTagProcessor {

    private SysConfigMapper sysConfigMapper;

    private static final String ATTRIBUTE_NAME = "sysc";
    private static final int PRECEDENCE = 10000;

    public SysConfigProcessor(final String dialectPrefix, final SysConfigMapper sysConfigMapper) {
        super(TemplateMode.HTML, dialectPrefix,
                null, false,
                ATTRIBUTE_NAME, true,
                PRECEDENCE, true);
        this.sysConfigMapper = sysConfigMapper;
    }

    @Override
    public void doProcess(ITemplateContext context, IProcessableElementTag tag,
                          AttributeName attributeName, String attributeValue,
                          IElementTagStructureHandler structureHandler) {
        // 配置类型 params[0], 配置属性 params[1], 前值 params[2], 后值 params[3], 标签 params[4]
        String[] params = attributeValue.split(",");

        // 获取到系统配置
        SysConfig entity = new SysConfig();
        entity.setType(params[0]);
        entity.setColumn(params[1]);
        entity = sysConfigMapper.selectOne(entity);

        String param = (String) ThymeleafFacade.evaluateExpression(context, params[2]);

        if (entity != null) {
            if (params.length == 5) {
                structureHandler.setAttribute(params[4], param + entity.getValue() + params[3]);
            }
        }
    }
}

/**
 * 地址
 */
class GetAreaProcessor extends AbstractAttributeTagProcessor {

    private AreaMapper areaMapper;

    private static final String ATTRIBUTE_NAME = "area";
    private static final int PRECEDENCE = 10000;

    public GetAreaProcessor(final String dialectPrefix, final AreaMapper areaMapper) {
        super(TemplateMode.HTML, dialectPrefix,
                null, false,
                ATTRIBUTE_NAME, true,
                PRECEDENCE, true);
        this.areaMapper = areaMapper;
    }

    @Override
    public void doProcess(ITemplateContext context, IProcessableElementTag tag,
                          AttributeName attributeName, String attributeValue,
                          IElementTagStructureHandler structureHandler) {
        // 1. 地址id [0]
        String[] params = attributeValue.split(",");

        // 获取地址
        Integer param = (Integer) ThymeleafFacade.evaluateExpression(context, params[0]);
        Area area = areaMapper.selectOne(new Area(param));

        structureHandler.setBody(area.getName(), false);
    }
}

/**
 * 自定义字符串处理
 */
class NumberProcessor extends AbstractAttributeTagProcessor {

    private static final String ATTRIBUTE_NAME = "count";
    private static final int PRECEDENCE = 10000;

    public NumberProcessor(final String dialectPrefix) {
        super(TemplateMode.HTML, dialectPrefix,
                null, false,
                ATTRIBUTE_NAME, true,
                PRECEDENCE, true);
    }

    @Override
    public void doProcess(ITemplateContext context, IProcessableElementTag tag,
                          AttributeName attributeName, String attributeValue,
                          IElementTagStructureHandler structureHandler) {
        Integer count = (Integer) ThymeleafFacade.evaluateExpression(context, attributeValue);
        if (count < 10000) {
            structureHandler.setBody(count.toString(), false);
        } else {
            String str = count.toString();
            str = String.format("%s.%sW", str.substring(0, str.length() - 4), str.substring(str.length() - 4, str.length() - 3));
            structureHandler.setBody(str, false);
        }
    }
}

class ThymeleafFacade {

    private ThymeleafFacade() {
        throw new UnsupportedOperationException();
    }

    public static String getRawValue(final IProcessableElementTag element, final AttributeName attributeName) {
        Validate.notNull(element, "element must not be null");
        Validate.notNull(attributeName, "attributeName must not be empty");

        final String rawValue = StringUtils.trim(element.getAttributeValue(attributeName));
        Validate.notEmpty(rawValue, "value of '" + attributeName + "' must not be empty");

        return rawValue;
    }

    public static String getRawValue(final IProcessableElementTag element, final String attributeName) {
        Validate.notNull(element, "element must not be null");
        Validate.notEmpty(attributeName, "attributeName must not be empty");

        final String rawValue = StringUtils.trim(element.getAttributeValue(attributeName));
        Validate.notEmpty(rawValue, "value of '" + attributeName + "' must not be empty");

        return rawValue;
    }

    public static Object evaluateExpression(ITemplateContext arguments, String expression) throws TemplateProcessingException {
        Validate.notNull(arguments, "arguments must not be null");
        Validate.notEmpty(expression, "expression must not be empty");

        final IStandardExpressionParser parser = new StandardExpressionParser();

        final IStandardExpression evaluableExpression = parser.parseExpression(arguments, expression);

        return evaluableExpression.execute(arguments);
    }

    public static List<Object> evaluateAsIterable(ITemplateContext arguments, String rawValue) throws TemplateProcessingException {
        Validate.notNull(arguments, "arguments must not be null");
        Validate.notEmpty(rawValue, "rawValue must not be empty");

        final Object evaluatedExpression = evaluateExpression(arguments, rawValue);

        return EvaluationUtils.evaluateAsList(evaluatedExpression);
    }

    public static List<Object> evaluateAsIterableOrRawValue(ITemplateContext arguments, String rawValue) {
        Validate.notNull(arguments, "arguments must not be null");
        Validate.notEmpty(rawValue, "rawValue must not be empty");

        final List<Object> result = new ArrayList<Object>();
        try {
            result.addAll(evaluateAsIterable(arguments, rawValue));
        } catch (TemplateProcessingException ex) {
            result.add(rawValue);
        }

        return Collections.unmodifiableList(result);
    }

    public static List<String> evaluateAsStringsWithDelimiter(ITemplateContext arguments, String rawValue, String delimiter) {
        Validate.notNull(arguments, "arguments must not be null");
        Validate.notEmpty(rawValue, "rawValue must not be empty");
        Validate.notEmpty(delimiter, "delimiter must not be empty");

        final List<String> result = new ArrayList<String>();
        final List<Object> iterates = evaluateAsIterableOrRawValue(arguments, rawValue);

        for (Object o : iterates) {
            result.addAll(Arrays.asList(org.thymeleaf.util.StringUtils.split(o, delimiter)));
        }

        return Collections.unmodifiableList(result);
    }
}